package edu.pku.sei.sct.primitives;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import edu.pku.sei.ocl.cst.CSTNode;
import edu.pku.sei.ocl.cst.InvCS;
import edu.pku.sei.ocl.cst.InvOrDefCS;
import edu.pku.sei.ocl.evaluation.Context;
import edu.pku.sei.ocl.parser.OCLLexer;
import edu.pku.sei.ocl.parser.OCLParser;
import edu.pku.sei.ocl.utilities.OCLConstant;
import edu.pku.sei.ocl.visitor.AbstractOCLVisitor;
import edu.pku.sei.sct.evaluation.SCTEnvironment;

public class EvaluateExpression extends AbstractPrimitive {
	static public final String ID = "EvaluateExpression";

	
	private AbstractOCLVisitor check = AbstractOCLVisitor.modelCheck;
	private AbstractOCLVisitor enforce = AbstractOCLVisitor.modelEnforce;
	private AbstractOCLVisitor imperative = AbstractOCLVisitor.expressionEnforce;

	private OCLLexer lexer = null;
	private OCLParser parser = null;
	
	private HashMap<List<String>,List<CSTNode>> exprsCache = new HashMap<List<String>,List<CSTNode>>();
	
	public EvaluateExpression(SCTEnvironment e) {
		super(e);
		lexer = new OCLLexer();
		parser = new OCLParser(lexer);
	}

	/**
	 * argv[0] = mode
	 * argv[1] = expressions
	 * argv[2] = context
	 */
	@SuppressWarnings("unchecked")
	@Override
	public Object execute(Object... argv) {
		String mode = (String) argv[0];
		List<String> expressions = (List<String>) argv[1];
		Context context = (Context) argv[2];
		List<CSTNode> cstexp = getExpressions(expressions);
		
		AbstractOCLVisitor visitor = null;
		
		String expMode = this.getSctEnvironment().resolveMode(mode);
		
		if("check".equals(expMode)) {
			visitor = check;
		} else if("enforce".equals(expMode)) {
			visitor = enforce;
		} else if("imperative".equals(expMode))
			visitor = imperative;
		
		context.setMode(mode);
		
		Context currentContext = context;
		
		Object returnValue = true;
		
		for(CSTNode e : cstexp) {
			Object[] result = (Object[]) e.accept(visitor, currentContext, true);
			if(result[0] instanceof Boolean || result[0] == OCLConstant.OCLInvalid) {
				if(result[0]==Boolean.TRUE)
					currentContext = (Context) result[1];
				else {
					returnValue = false;
					break;
				}
			} else {
				System.out.println("evaluate expressions: enter statement mode");
				returnValue = result[0];
				currentContext = (Context) result[1];
			}
		}
		
		context.applyPatch(currentContext);
		
		return returnValue;
	}
	
	public List<CSTNode> getExpressions(List<String> exprs) {
		List<CSTNode> result = exprsCache.get(exprs);
		if(result == null) {
			result = new ArrayList<CSTNode>();
			exprsCache.put(exprs, result);
			
			for(String str : exprs) {
				try {
					if(str.charAt(0)=='['&&str.charAt(str.length()-1)==']')
						str = str.substring(1, str.length()-1);
					
					String buf = "inv : " + str;
					lexer.reset(buf.toCharArray(), "OCL");
					parser.reset(lexer.getILexStream());
					lexer.lexer(parser.getIPrsStream());
					
					InvCS exp = (InvCS) parser.parser();
					result.add(exp.getExpressionCS());
				}catch(Exception e) {
					e.printStackTrace();
				}
			}
		}
		return result;
	}

	@Override
	public String getID() {
		return ID;
	}

}
